package jcogo;

import java.text.DecimalFormat;

/**
* Point is a class to hold a surveying point. It contains fields for
* northing (x), easting (y), elevation (z), as well as number and a
* short description. There are also some utility methods.
* <p>
* This file is part of jcogo.
* <p>
* jcogo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* <p>
* jcogo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with jcogo. If not, see <a href="http://www.gnu.org/licenses/">
* http://www.gnu.org/licenses/</a>
*
* @author Nathaniel Schwartz
*
* Copyright 2010 Nathaniel Schwartz.
*/

public class Point implements Comparable {

  /**
   * The identifying integer of this Point.
   */
  private int number;

  /**
   * This Point's northing (vertical) coordinate.
   */
  private double x;

  /**
   * This Point's easting (horizontal) coordinate.
   */
  private double y;
  
  /**
   * This Point's vertical (elevation) coordinate.
   */
  private double z;

  /**
   * This Point's description.
   */
  private String description;

  /**
   * Just a way to format double values to 4 decimal places.
   */
  private DecimalFormat f;

  /**
   * Constructs a Point with each field provided explicitly.
   * @param num the Point number.
   * @param x the northing value.
   * @param y the easting value.
   * @param z the elevation value.
   * @param desc the description.
   */
  public Point(int num, double x, double y, double z, String desc) {
    this.number = num;
    this.x = x;
    this.y = y;
    this.z = z;
    this.description = desc;
    f = new DecimalFormat("#.####");
  }

  /**
   * This default constructor assigns default values to the Point's fields,
   * but the Point number (-1) should be useful as a check to see if the
   * Point is valid.
   */
  public Point() {
    this.number = -1;
    this.x = 0;
    this.y = 0;
    this.z = 0;
    this.description = "invalid point";
    f = new DecimalFormat("#.####");
  }

  /**
   * Sets the Point number.
   * @param number the Point number.
   */
  public void setNumber(int number) {
    this.number = number;
  }

  /**
   * Gets the Point number.
   * @return the Point number.
   */
  public int getNumber() {
    return this.number;
  }

  /**
   * Sets the Point's northing value.
   * @param x the Point's northing value.
   */
  public void setX(double x) {
    this.x = x;
  }

  /**
   * Gets the Point's northing value.
   * @return the Point's northing value.
   */
  public double getX() {
    return this.x;
  }

  /**
   * Sets the Point's easting value.
   * @param y the Point's easting value.
   */
  public void setY(double y) {
    this.y = y;
  }

  /**
   * Gets the Point's easting value.
   * @return the Point's eating value.
   */
  public double getY() {
    return this.y;
  }

  /**
   * Set's the Points elevation value.
   * @param z the Point's elevation value.
   */
  public void setZ(double z) {
    this.z = z;
  }

  /**
   * Gets the Point's elevation value.
   * @return the Point's elevation value.
   */
  public double getZ() {
    return this.z;
  }

  /**
   * Sets the Point's description.
   * @param description the Point's description.
   */
  public void setDescription(String description) {
    this.description = description;
  }

  /**
   * Gets the Point's description.
   * @return the Point's description.
   */
  public String getDescription() {
    return this.description;
  }

  /**
   * Calculates the azimuth between this Point and another Point.
   * @param p the Point toward which the azimuth will be found.
   * @return the azimuth between this Point and the Point p.
   */
  public Angle azimuthBetween(Point p) {
    double x = p.getX() - this.x;
    double y = p.getY() - this.y;
    double azimuth = (Math.atan2(y, x)) * 180.0/Math.PI;
    if (azimuth < 0) azimuth = 360 + azimuth;
    return new Angle(azimuth);
  }

  /**
   * Calculates the distance between this Point and another Point.
   * @param p the Point from which the distance is calculated.
   * @return the distance to Point p.
   */
  public double distanceBetween(Point p) {
    double x = p.getX() - this.x;
    double y = p.getY() - this.y;
    return Double.valueOf(f.format(Math.sqrt(x*x + y*y)));
  }

  /**
   * Prints this Point in a nice format.
   * @return the String representation of this Point.
   */
  public String toString() {
    return new String(
      "" + number + "\t" + Double.valueOf(f.format(x))
      + "\t" + Double.valueOf(f.format(y))
      + "\t" + Double.valueOf(f.format(z)) + "\t"
      + description + "\n");
  }

  /**
   * Compares this Point to another point using the numbers as the basis of
   * comparison.
   * @return -1 if this Point is smaller than the other, 0 if equal, and 1 if
   *    this Point is larger than the other.
   */
  public int compareTo(Object o) {
    Point p = (Point) o;
    if (this.number < p.getNumber()) return -1;
    else if (this.number > p.getNumber()) return 1;
    return 0;
  }
}
